//
// generic.cs: Generics support
//
// Authors: Martin Baulig (martin@ximian.com)
//          Miguel de Icaza (miguel@ximian.com)
//          Marek Safar (marek.safar@gmail.com)
//
// Dual licensed under the terms of the MIT X11 or GNU GPL
//
// Copyright 2001, 2002, 2003 Ximian, Inc (http://www.ximian.com)
// Copyright 2004-2008 Novell, Inc
// Copyright 2011 Xamarin, Inc (http://www.xamarin.com)
//

using System;
using System.Collections.Generic;
using System.Text;
using System.Linq;
using MetaType = System.Type;
using System.Reflection;
using System.Reflection.Emit;

namespace Teal.Compiler {
    public enum Variance
	{
		//
		// Don't add or modify internal values, they are used as -/+ calculation signs
		//
		None			= 0,
		Covariant		= 1,
		Contravariant	= -1
	}

    //
	// A type parameter for a generic type or generic method definition
	//
	public class TypeParameter : MemberDefinition, ITypeDefinition
	{
		GenericTypeParameterBuilder builder;
		readonly Teal.Compiler.MemberReference.GenericParameter spec;

		public TypeParameter (int index, MemberName name, MemberAnnotationList attrs, Variance Variance) 
		{
            this.spec = new Teal.Compiler.MemberReference.GenericParameter(null, index, this, Variance, null);
		}

		//
		// Used by parser
		//

	    public TypeParameter (Teal.Compiler.MemberReference.GenericParameter spec, TypeReference parentSpec, MemberName name, MemberAnnotationList attrs)    {
			
		}
		
		#region Properties

		public PackageDefinition DeclaringAssembly {
			get	{
				return containerModule.DeclaringAssembly;
			}
		}

	    bool ITypeDefinition.IsComImport {
			get {
				return false;
			}
		}

		public bool IsMethodTypeParameter {
			get {
				return spec.IsMethodOwned;
			}
		}

		bool ITypeDefinition.IsTypeForwarder {
			get {
				return false;
			}
		}

		bool ITypeDefinition.IsCyclicTypeForwarder {
			get {
				return false;
			}
		}

		public string name {
			get {
				return MemberName.Name;
			}
		}

		public string Namespace {
			get {
				return null;
			}
		}

		public Teal.Compiler.MemberReference.GenericParameter Type {
			get {
				return spec;
			}
		}

		public int TypeParametersCount {
			get {
				return 0;
			}
		}

		public Teal.Compiler.MemberReference.GenericParameter[] TypeParameters {
			get {
				return null;
			}
		}

		public Variance Variance {
			get {
				return spec.Variance;
			}
		}

		#endregion

		//
		// This is called for each part of a partial generic type definition.
		//
		// If partial type parameters constraints are not null and we don't
		// already have constraints they become our constraints. If we already
		// have constraints, we must check that they're same.
		//

	    public override void ApplyAttributeBuilder (MemberAnnotation a, MethodReference ctor, byte[] cdata, PredefinedAttributes pa)
		{
			builder.SetCustomAttribute ((ConstructorInfo) ctor.GetMetaInfo (), cdata);
		}

	    public TypeParameter CreateHoistedCopy (TypeReference declaringSpec)
		{
			return new TypeParameter (spec, declaringSpec, MemberName, null);
		}

		public void Define (GenericTypeParameterBuilder type)
		{
			this.builder = type;
			spec.SetMetaInfo (type);
		}

	    public TypeReference GetAttributeCoClass ()
		{
			return null;
		}

		public string GetAttributeDefaultMember ()
		{
			throw new NotSupportedException ();
		}

		public AttributeUsageAttribute GetAttributeUsage (PredefinedAttribute pa)
		{
			throw new NotSupportedException ();
		}

	    public override string GetSignatureForError ()
		{
			return MemberName.Name;
		}

		bool ITypeDefinition.IsInternalAsPublic (PackageDefinition assembly)
		{
			return spec.MemberDefinition2.DeclaringAssembly == assembly;
		}

		public void LoadMembers (TypeReference declaringType, bool onlyTypes, ref MemberCache cache)
		{
			throw new NotSupportedException ("Not supported for compiled definition");
		}

		//
		// Resolves all type parameter constraints
		//

	    public override void defineMember() {
            throw new NotImplementedException();
        }

        public override void resolveMember() {
            throw new NotImplementedException();
        }

        public override void resolveBaseTypes() {
            throw new NotImplementedException();
        }

        protected override MemberType memberType {
            get { throw new NotImplementedException(); }
        }

        public override void resolveBody() {
            throw new NotImplementedException();
        }

        public override void createTypeBuilder() {
            throw new NotImplementedException();
        }

        public override void emitBody() {
            throw new NotImplementedException();
        }

        public override void closeTypeBuilder() {
            throw new NotImplementedException();
        }
    }

    public abstract partial class MemberReference {
        public partial class GenericParameter : TypeReference {

            public new static readonly Teal.Compiler.MemberReference.GenericParameter[] EmptyTypes = new Teal.Compiler.MemberReference.GenericParameter[0];

            private Variance variance;
            private int tp_pos;
            private TypeReference[] targs;
            private TypeReference[] ifaces_defined;
            private TypeReference effective_base;

            //
            // Creates type owned type parameter
            //
            public GenericParameter(TypeReference declaringType, int index, ITypeDefinition definition, Variance variance, MetaType info)
                : base(MemberKind.TypeParameter, declaringType, definition, info, Modifiers.@public) {
                this.variance = variance;
                resolveFlags &= ~ResolveFlags.Obsolete_Undetected;
                tp_pos = index;
            }

            //
            // Creates method owned type parameter
            //
            public GenericParameter(int index, ITypeDefinition definition, Variance variance, MetaType info)
                : this(null, index, definition, variance, info) { }

            #region Properties

            public int DeclaredPosition {
                get {
                    return tp_pos;
                }
                set {
                    tp_pos = value;
                }
            }

            public bool HasTypeConstraint {
                get {
                    var bt = baseClass.BuiltinType;
                    return bt != BuiltinTypeSpec.Type.Object && bt != BuiltinTypeSpec.Type.ValueType;
                }
            }

            //public override List<TypeReference> baseInterfaces {
            //    get {
            //        if ((resolveFlags & ResolveFlags.InterfacesExpanded) == 0) {
            //            if (_baseInterfaces != null) {
            //                if (ifaces_defined == null)
            //                    ifaces_defined = _baseInterfaces.ToArray ();

            //                for (int i = 0; i < ifaces_defined.Length; ++i ) {
            //                    var iface_type = ifaces_defined[i];
            //                    var td = iface_type.MemberDefinition as TypeDefinition;
            //                    if (td != null)
            //                        td.DoExpandBaseInterfaces ();

            //                    if (iface_type.baseInterfaces != null) {
            //                        for (int ii = 0; ii < iface_type.baseInterfaces.Count; ++ii) {
            //                            var ii_iface_type = iface_type.baseInterfaces [ii];
            //                            addInterface (ii_iface_type);
            //                        }
            //                    }
            //                }
            //            } else if (ifaces_defined == null) {
            //                ifaces_defined = _baseInterfaces == null ? TypeReference.EmptyTypes : _baseInterfaces.ToArray ();
            //            }

            //            //
            //            // Include all base type interfaces too, see ImportTypeBase for details
            //            //
            //            if (baseClass != null) {
            //                var td = baseClass.MemberDefinition as TypeDefinition;
            //                if (td != null)
            //                    td.DoExpandBaseInterfaces ();

            //                if (baseClass.baseInterfaces != null) {
            //                    foreach (var iface in baseClass.baseInterfaces) {
            //                        addInterface (iface);
            //                    }
            //                }
            //            }

            //            resolveFlags |= ResolveFlags.InterfacesExpanded;
            //        }

            //        return _baseInterfaces;
            //    }
            //}

            //
            // Unexpanded interfaces list
            //
            public TypeReference[] InterfacesDefined {
                get {
                    if (ifaces_defined == null) {
                        ifaces_defined = baseInterfaces == null ? TypeReference.EmptyTypes : baseInterfaces.ToArray();
                    }

                    return ifaces_defined.Length == 0 ? null : ifaces_defined;
                }
                set {
                    ifaces_defined = value;
                    if (value != null && value.Length != 0)
                        baseInterfaces = new List<TypeReference>(value);
                }
            }

            public bool IsConstrained {
                get {
                    return attributes != 0 || baseInterfaces != null || targs != null || HasTypeConstraint;
                }
            }

            //
            // Returns whether the type parameter is known to be a reference type
            //
            public new bool IsReferenceType {
                get {
                    if ((attributes & (Attributes.classConstraint | Attributes.structConstraint)) != 0) {
                        return (attributes & Attributes.classConstraint) != 0;
                    }

                    //
                    // Full check is needed (see IsValueType for details)
                    //
                    if (HasTypeConstraint && TypeReference.IsReferenceType(baseClass))
                        return true;

                    if (targs != null) {
                        foreach (var ta in targs) {
                            //
                            // Secondary special constraints are ignored (I am not sure why)
                            //
                            var tp = ta as MemberReference.GenericParameter;
                            if (tp != null && (tp.attributes & (MemberReference.GenericParameter.Attributes.classConstraint | MemberReference.GenericParameter.Attributes.structConstraint)) != 0)
                                continue;

                            if (TypeReference.IsReferenceType(ta))
                                return true;
                        }
                    }

                    return false;
                }
            }

            //
            // Returns whether the type parameter is known to be a value type
            //
            public new bool IsValueType {
                get {
                    //
                    // Even if structs/enums cannot be used directly as constraints
                    // they can apear as constraint type when inheriting base constraint
                    // which has dependant type parameter constraint which has been
                    // inflated using value type
                    //
                    // class A : B<int> { override void Foo<U> () {} }
                    // class B<T> { virtual void Foo<U> () where U : T {} }
                    //
                    if (memberTypeConstraint == Attributes.structConstraint)
                        return true;

                    if (targs != null) {
                        foreach (var ta in targs) {
                            if (TypeReference.IsValueType(ta))
                                return true;
                        }
                    }

                    return false;
                }
            }

            public bool IsMethodOwned {
                get {
                    return containerType == null;
                }
            }

            //
            // Types used to inflate the generic type
            //
            public new TypeReference[] TypeArguments {
                get {
                    return targs;
                }
                set {
                    targs = value;
                }
            }

            public Variance Variance {
                get {
                    return variance;
                }
            }

            #endregion

            //
            // Finds effective base class. The effective base class is always a class-type
            //
            public TypeReference GetEffectiveBase() {
                if (memberTypeConstraint == Attributes.structConstraint)
                    return baseClass;

                //
                // If T has a class-type constraint C but no type-parameter constraints, its effective base class is C
                //
                if (baseClass != null && targs == null) {
                    //
                    // If T has a constraint V that is a value-type, use instead the most specific base type of V that is a class-type.
                    // 
                    // LAMESPEC: Is System.ValueType always the most specific base type in this case?
                    //
                    // Note: This can never happen in an explicitly given constraint, but may occur when the constraints of a generic method
                    // are implicitly inherited by an overriding method declaration or an explicit implementation of an interface method.
                    //
                    return baseClass.IsStruct ? baseClass.baseClass : baseClass;
                }

                if (effective_base != null)
                    return effective_base;

                var types = new TypeReference[HasTypeConstraint ? targs.Length + 1 : targs.Length];

                for (int i = 0; i < targs.Length; ++i) {
                    var t = targs[i];

                    // Same issue as above, inherited constraints can be of struct type
                    if (t.IsStruct) {
                        types[i] = t.baseClass;
                        continue;
                    }

                    var tps = t as Teal.Compiler.MemberReference.GenericParameter;
                    types[i] = tps != null ? tps.GetEffectiveBase() : t;
                }

                if (HasTypeConstraint)
                    types[types.Length - 1] = baseClass;

                return effective_base = Convert.FindMostEncompassedType(types);
            }

            public override string GetSignatureForError() {
                return name;
            }

            public static Teal.Compiler.MemberReference.GenericParameter[] InflateConstraints<T>(Teal.Compiler.MemberReference.GenericParameter[] tparams, Func<T, TypeParameterInflator> inflatorFactory, T arg) {
                Teal.Compiler.MemberReference.GenericParameter[] constraints = null;
                TypeParameterInflator? inflator = null;

                for (int i = 0; i < tparams.Length; ++i) {
                    var tp = tparams[i];
                    if (tp.HasTypeConstraint || tp.InterfacesDefined != null || tp.TypeArguments != null) {
                        if (constraints == null) {
                            constraints = new Teal.Compiler.MemberReference.GenericParameter[tparams.Length];
                            Array.Copy(tparams, constraints, constraints.Length);
                        }

                        //
                        // Using a factory to avoid possibly expensive inflator build up
                        //
                        if (inflator == null)
                            inflator = inflatorFactory(arg);

                        //constraints[i] = (Teal.Compiler.MemberReference.GenericTypeParameterReference) constraints[i].InflateMember (inflator.Value);
                        constraints[i] = (Teal.Compiler.MemberReference.GenericParameter) constraints[i].inflateMember(inflator.Value.type);
                    }
                }

                if (constraints == null)
                    constraints = tparams;

                return constraints;
            }

            public bool IsConvertibleToInterface(TypeReference iface) {
                if (baseInterfaces != null) {
                    foreach (var t in baseInterfaces) {
                        if (t == iface)
                            return true;
                    }
                }

                if (TypeArguments != null) {
                    foreach (var t in TypeArguments) {
                        var tps = t as Teal.Compiler.MemberReference.GenericParameter;
                        if (tps != null) {
                            if (tps.IsConvertibleToInterface(iface))
                                return true;

                            continue;
                        }

                        if (t.ImplementsInterface(iface, false))
                            return true;
                    }
                }

                return false;
            }

            public static bool HasAnyTypeParameterTypeConstrained(MethodDefinition md) {
                var tps = md.TypeParameters;
                for (int i = 0; i < md.TypeParametersCount; ++i) {
                    if (tps[i].hasConstraints) {
                        return true;
                    }
                }

                return false;
            }

            public static bool HasAnyTypeParameterConstrained(MethodDefinition md) {
                var tps = md.TypeParameters;
                for (int i = 0; i < md.TypeParametersCount; ++i) {
                    if (tps[i].IsConstrained) {
                        return true;
                    }
                }

                return false;
            }

            public bool HasDependencyOn(TypeReference type) {
                if (TypeArguments != null) {
                    foreach (var targ in TypeArguments) {
                        if (TypeSpecComparer.Override.IsEqual(targ, type))
                            return true;

                        var tps = targ as Teal.Compiler.MemberReference.GenericParameter;
                        if (tps != null && tps.HasDependencyOn(type))
                            return true;
                    }
                }

                return false;
            }

            public override TypeReference Mutate(TypeParameterMutator mutator) {
                return mutator.Mutate(this);
            }
        }
    }

    public struct TypeParameterInflator
	{
		public readonly TypeReference type;

        public TypeParameterInflator(TypeReference type, Teal.Compiler.MemberReference.GenericParameter[] tparams, TypeReference[] targs)
		{
			this.type = type;
		}
    }

	//
	// Before emitting any code we have to change all MVAR references to VAR
	// when the method is of generic type and has hoisted variables
	//
	public class TypeParameterMutator
	{
		readonly TypeParameters mvar;
		readonly TypeParameters var;
		readonly Teal.Compiler.MemberReference.GenericParameter[] src;
		Dictionary<TypeReference, TypeReference> mutated_typespec;

		public TypeParameterMutator (TypeParameters mvar, TypeParameters var)
		{
			if (mvar.Count != var.Count)
				throw new ArgumentException ();

			this.mvar = mvar;
			this.var = var;
		}

		public TypeParameterMutator (Teal.Compiler.MemberReference.GenericParameter[] srcVar, TypeParameters destVar)
		{
			if (srcVar.Length != destVar.Count)
				throw new ArgumentException ();

			this.src = srcVar;
			this.var = destVar;
		}

		public static TypeReference GetMemberDeclaringType (TypeReference type)
		{
			if (type is GenericTypeReference) {
				if (type.containerType == null)
					return type.GetDefinition ();

				var parent = GetMemberDeclaringType (type.containerType);
				type = MemberCache.GetMember<TypeReference> (parent, type);
			}

			return type;
		}

		public TypeReference Mutate (TypeReference ts)
		{
			TypeReference value;
			if (mutated_typespec != null && mutated_typespec.TryGetValue (ts, out value))
				return value;

			value = ts.Mutate (this);
			if (mutated_typespec == null)
				mutated_typespec = new Dictionary<TypeReference, TypeReference> ();

			mutated_typespec.Add (ts, value);
			return value;
		}
        
		public Teal.Compiler.MemberReference.GenericParameter Mutate (Teal.Compiler.MemberReference.GenericParameter tp)
		{
			if (mvar != null) {
				for (int i = 0; i < mvar.Count; ++i) {
					if (mvar[i].Type == tp)
						return var[i].Type;
				}
			} else {
				for (int i = 0; i < src.Length; ++i) {
					if (src[i] == tp)
						return var[i].Type;
				}
			}

			return tp;
		}

		public TypeReference[] Mutate (TypeReference[] targs)
		{
			TypeReference[] mutated = new TypeReference[targs.Length];
			bool changed = false;
			for (int i = 0; i < targs.Length; ++i) {
				mutated[i] = Mutate (targs[i]);
				changed |= targs[i] != mutated[i];
			}

			return changed ? mutated : targs;
		}
	}

	/// <summary>
	///   A TypeExpr which already resolved to a type parameter.
	/// </summary>
	public class TypeParameterExpr : TypeExpression
	{
		public TypeParameterExpr (TypeParameter type_parameter, Location2 loc)
			: base (type_parameter.Type, loc)
		{
			this.eclass = ExprClass.TypeParameter;
		}
	}

    public partial class GenericTypeReference
	{
		TypeReference[] targs;
		Teal.Compiler.MemberReference.GenericParameter[] constraints;
		readonly TypeReference open_type;

        public GenericTypeReference(TypeReference openType, TypeReference declaringType, TypeReference[] targs)
			: base (0, null)
		{
			if (targs == null)
				throw new ArgumentNullException ("targs");

			this.resolveFlags &= ~SharedStateFlags;
			this.resolveFlags |= (openType.resolveFlags & SharedStateFlags);
             
			this.open_type = openType;
			this.targs = targs;

			foreach (var arg in targs) {
				if (arg.HasDynamicElement || arg.BuiltinType == BuiltinTypeSpec.Type.Dynamic) {
					resolveFlags |= Teal.Compiler.ResolveFlags.HasDynamicElement;
					break;
				}
			}

			if (open_type.Kind == MemberKind.MissingType)
				MemberCache = MemberCache.Empty;

			if ((open_type.modifiers & Modifiers.COMPILER_GENERATED) != 0)
                resolveFlags |= Teal.Compiler.ResolveFlags.ConstraintsChecked;
		}

		#region Properties

		//
		// Inflated type parameters with constraints array, mapping with type arguments is based on index
		//
		public Teal.Compiler.MemberReference.GenericParameter[] Constraints {
			get {
				if (constraints == null) {
					constraints = Teal.Compiler.MemberReference.GenericParameter.InflateConstraints (MemberDefinition2.TypeParameters, l => default(TypeParameterInflator) , this);
				}

				return constraints;
			}
		}

		//
		// Used to cache expensive constraints validation on constructed types
		//
		public bool HasConstraintsChecked {
			get {
                return (resolveFlags & Teal.Compiler.ResolveFlags.ConstraintsChecked) != 0;
			}
			set {
                resolveFlags = value ? resolveFlags | Teal.Compiler.ResolveFlags.ConstraintsChecked : resolveFlags & ~Teal.Compiler.ResolveFlags.ConstraintsChecked;
			}
		}

		public override bool IsExpressionTreeType {
			get {
                return (open_type.resolveFlags & Teal.Compiler.ResolveFlags.InflatedExpressionType) != 0;
			}
		}

		public override bool IsArrayGenericInterface {
			get {
                return (open_type.resolveFlags & Teal.Compiler.ResolveFlags.GenericIterateInterface) != 0;
			}
		}

		public override bool IsGenericTask {
			get {
                return (open_type.resolveFlags & Teal.Compiler.ResolveFlags.GenericTask) != 0;
			}
		}

		public override bool IsNullableType {
			get {
                return (open_type.resolveFlags & Teal.Compiler.ResolveFlags.InflatedNullableType) != 0;
			}
		}

		//
		// Types used to inflate the generic  type
		//
		public override TypeReference[] TypeArguments {
			get {
				return targs;
			}
		}

		#endregion

		public static bool ContainsTypeParameter (TypeReference type)
		{
			if (type.Kind == MemberKind.TypeParameter)
				return true;

			var element_container = type as ElementTypeSpec;
			if (element_container != null)
				return ContainsTypeParameter (element_container.Element);

			foreach (var t in type.TypeArguments) {
				if (ContainsTypeParameter (t)) {
					return true;
				}
			}

			return false;
		}

        public override ObsoleteAttribute GetAttributeObsolete ()
		{
			return open_type.GetAttributeObsolete ();
		}

		public override TypeReference GetDefinition ()
		{
			return open_type;
		}

		public override string GetSignatureForError ()
		{
			if (IsNullableType)
				return targs[0].GetSignatureForError () + "?";

			return base.GetSignatureForError ();
		}

		protected override string GetTypeNameSignature ()
		{
			if (targs.Length == 0 || MemberDefinition2 is AnonymousTypeClass)
				return null;

			return "<" + TypeManager.CSharpName (targs) + ">";
		}

		public override TypeReference Mutate (TypeParameterMutator mutator)
		{
			var targs = TypeArguments;
			if (targs != null)
				targs = mutator.Mutate (targs);

			var decl = containerType;
			if (IsNested && containerType.IsGenericOrParentIsGeneric)
				decl = mutator.Mutate (decl);

			if (targs == TypeArguments && decl == containerType)
				return this;

			var mutated = (GenericTypeReference) MemberwiseClone ();
			if (decl != containerType) {
				// Gets back MethodInfo in case of metaInfo was inflated
				//mutated.info = MemberCache.GetMember<TypeSpec> (DeclaringType.GetDefinition (), this).info;

				mutated.containerType = decl;
				mutated.resolveFlags |= Teal.Compiler.ResolveFlags.PendingMetaInflate;
			}

			if (targs != null) {
				mutated.targs = targs;
				mutated.metaInfo = null;
			}

			return mutated;
		}
	}


	//
	// Tracks the type arguments when instantiating a generic type. It's used
	// by both type arguments and type parameters
	//
	public class TypeArguments
	{
		List<FullNamedExpression> args;
		TypeReference[] atypes;

		public TypeArguments (params FullNamedExpression[] types)
		{
			this.args = new List<FullNamedExpression> (types);
		}

		public void Add (FullNamedExpression type)
		{
			args.Add (type);
		}

		/// <summary>
		///   We may only be used after Resolve() is called and return the fully
		///   resolved types.
		/// </summary>
		// TODO: Not needed, just return type from resolve
		public TypeReference[] Arguments {
			get {
				return atypes;
			}
			set {
				atypes = value;
			}
		}

		public int Count {
			get {
				return args.Count;
			}
		}

		public virtual bool IsEmpty {
			get {
				return false;
			}
		}

	    public string GetSignatureForError()
		{
			StringBuilder sb = new StringBuilder ();
			for (int i = 0; i < Count; ++i) {
				var expr = args[i];
				if (expr != null)
					sb.Append (expr.GetSignatureForError ());

				if (i + 1 < Count)
					sb.Append (',');
			}

			return sb.ToString ();
		}

		public TypeArguments Clone ()
		{
			TypeArguments copy = new TypeArguments ();
			foreach (var ta in args)
				copy.args.Add (ta);

			return copy;
		}
	}

	public class UnboundTypeArguments : TypeArguments
	{
		public UnboundTypeArguments (int arity)
			: base (new FullNamedExpression[arity])
		{
		}

		public override bool IsEmpty {
			get {
				return true;
			}
		}

	}

	public class TypeParameters
	{
		List<TypeParameter> names;
		Teal.Compiler.MemberReference.GenericParameter[] types;

		public TypeParameters ()
		{
			names = new List<TypeParameter> ();
		}

		public TypeParameters (int count)
		{
			names = new List<TypeParameter> (count);
		}

		#region Properties

		public int Count {
			get {
				return names.Count;
			}
		}

		public Teal.Compiler.MemberReference.GenericParameter[] Types {
			get {
				return types;
			}
		}

		#endregion

		public void Add (TypeParameter tparam)
		{
			names.Add (tparam);
		}

		public TypeParameter this[int index] {
			get {
				return names [index];
			}
			set {
				names[index] = value;
			}
		}

		public TypeParameter Find (string name)
		{
			foreach (var tp in names) {
				if (tp.name == name)
					return tp;
			}

			return null;
		}

	    public string GetSignatureForError ()
		{
			StringBuilder sb = new StringBuilder ();
			for (int i = 0; i < Count; ++i) {
				if (i > 0)
					sb.Append (',');

				var name = names[i];
				if (name != null)
					sb.Append (name.GetSignatureForError ());
			}

			return sb.ToString ();
		}

	}

	//
	// A type expression of generic type with type arguments
	//
	class GenericTypeExpr : TypeExpr
	{
		TypeArguments args;
		TypeReference open_type;

		/// <summary>
		///   Instantiate the generic type `t' with the type arguments `args'.
		///   Use this constructor if you already know the fully resolved
		///   generic type.
		/// </summary>		
		public GenericTypeExpr (TypeReference open_type, TypeArguments args, Location2 l)
		{
			this.open_type = open_type;
			loc = l;
			this.args = args;
		}

		public override string GetSignatureForError ()
		{
			return type2.GetSignatureForError ();
		}

		public override bool Equals (object obj)
		{
			GenericTypeExpr cobj = obj as GenericTypeExpr;
			if (cobj == null)
				return false;

			if ((type2 == null) || (cobj.type2 == null))
				return false;

			return type2 == cobj.type2;
		}

		public override int GetHashCode ()
		{
			return base.GetHashCode ();
		}
	}

	//
	// Generic type with unbound type arguments, used for typeof (G<,,>)
	//
	class GenericOpenTypeExpr : TypeExpression
	{
		public GenericOpenTypeExpr (TypeReference type, /*UnboundTypeArguments args,*/ Location2 loc)
			: base (type.GetDefinition (), loc)
		{
		}
	}

	struct ConstraintChecker
	{
		IMemberContext mc;
		bool recursive_checks;

		public ConstraintChecker (IMemberContext ctx)
		{
			this.mc = ctx;
			recursive_checks = false;
		}

		//
		// Checks the constraints of open generic type against type
		// arguments. This version is used for types which could not be
		// checked immediatelly during construction because the type
		// hierarchy was not yet fully setup (before Emit phase)
		//
		public static bool Check (IMemberContext mc, TypeReference type, Location2 loc)
		{
			//
			// Check declaring type first if there is any
			//
			if (type.containerType != null && !Check (mc, type.containerType, loc))
				return false;

			while (type is ElementTypeSpec)
				type = ((ElementTypeSpec) type).Element;

			if (type.genericParameterCount == 0)
				return true;

			var gtype = type as GenericTypeReference;
			if (gtype == null)
				return true;

			var constraints = gtype.Constraints;
			if (constraints == null)
				return true;

			if (gtype.HasConstraintsChecked)
				return true;

			var cc = new ConstraintChecker (mc);
			cc.recursive_checks = true;

			if (cc.CheckAll (gtype.GetDefinition (), type.TypeArguments, constraints, loc)) {
				gtype.HasConstraintsChecked = true;
				return true;
			}

			return false;
		}

		//
		// Checks all type arguments againts type parameters constraints
		// NOTE: It can run in probing mode when `this.mc' is null
		//
		public bool CheckAll (MemberReference context, TypeReference[] targs, Teal.Compiler.MemberReference.GenericParameter[] tparams, Location2 loc)
		{
			for (int i = 0; i < tparams.Length; i++) {
				var targ = targs[i];
				
				if (!recursive_checks)
					continue;

				if (!Check (mc, targ, loc))
					return false;
			}

			return true;
		}

	}

	//
	// Implements C# type inference
	//
	class TypeInference
	{
		//
		// Tracks successful rate of type inference
		//
		int score = int.MaxValue;
		readonly Arguments arguments;
		readonly int arg_count;

		public TypeInference (Arguments arguments)
		{
			this.arguments = arguments;
			if (arguments != null)
				arg_count = arguments.Count;
		}

		public int InferenceScore {
			get {
				return score;
			}
		}

		public TypeReference[] InferMethodArguments (ResolveContext ec, MethodReference method)
		{
			var method_generic_args = method.GenericDefinition.TypeParameters;
			TypeInferenceContext context = new TypeInferenceContext (method_generic_args);
			if (!context.UnfixedVariableExists)
				return TypeReference.EmptyTypes;

			AParametersCollection pd = method.Parameters;
			if (!InferInPhases (ec, context, pd))
				return null;

			return context.InferredTypeArguments;
		}

		//
		// Implements method type arguments inference
		//
		bool InferInPhases (ResolveContext ec, TypeInferenceContext tic, AParametersCollection methodParameters)
		{
			int params_arguments_start;
			if (methodParameters.HasParams) {
				params_arguments_start = methodParameters.Count - 1;
			} else {
				params_arguments_start = arg_count;
			}

			TypeReference [] ptypes = methodParameters.Types;
			
			//
			// The first inference phase
			//
			TypeReference method_parameter = null;
			for (int i = 0; i < arg_count; i++) {
				Argument a = arguments [i];
				if (a == null)
					continue;
				
				if (i < params_arguments_start) {
					method_parameter = methodParameters.Types [i];
				} else if (i == params_arguments_start) {
					if (arg_count == params_arguments_start + 1 && TypeManager.HasElementType (a.Type))
						method_parameter = methodParameters.Types [params_arguments_start];
					else
						method_parameter = TypeManager.GetElementType (methodParameters.Types [params_arguments_start]);

					ptypes = (TypeReference[]) ptypes.Clone ();
					ptypes [i] = method_parameter;
				}

				//
				// When a lambda expression, an anonymous method
				// is used an explicit argument type inference takes a place
				//
				AnonymousMethodExpression am = a.Expr as AnonymousMethodExpression;
				if (am != null) {
					if (am.ExplicitTypeInference (tic, method_parameter))
						--score; 
					continue;
				}

				if (a.IsByRef) {
					score -= tic.ExactInference (a.Type, method_parameter);
					continue;
				}

				if (a.Expr.Type == InternalType.NullLiteral)
					continue;

				if (TypeReference.IsValueType (method_parameter)) {
					score -= tic.LowerBoundInference (a.Type, method_parameter);
					continue;
				}

				//
				// Otherwise an output type inference is made
				//
				score -= tic.OutputTypeInference (ec, a.Expr, method_parameter);
			}

			//
			// Part of the second phase but because it happens only once
			// we don't need to call it in cycle
			//
			bool fixed_any = false;
			if (!tic.FixIndependentTypeArguments (ec, ptypes, ref fixed_any))
				return false;

			return DoSecondPhase (ec, tic, ptypes, !fixed_any);
		}

		bool DoSecondPhase (ResolveContext ec, TypeInferenceContext tic, TypeReference[] methodParameters, bool fixDependent)
		{
			bool fixed_any = false;
			if (fixDependent && !tic.FixDependentTypes (ec, ref fixed_any))
				return false;

			// If no further unfixed type variables exist, type inference succeeds
			if (!tic.UnfixedVariableExists)
				return true;

			if (!fixed_any && fixDependent)
				return false;
			
			// For all arguments where the corresponding argument output types
			// contain unfixed type variables but the input types do not,
			// an output type inference is made
			for (int i = 0; i < arg_count; i++) {
				
				// Align params arguments
				TypeReference t_i = methodParameters [i >= methodParameters.Length ? methodParameters.Length - 1: i];
				
				if (!t_i.IsDelegate) {
					if (!t_i.IsExpressionTreeType)
						continue;

					t_i = TypeManager.GetTypeArguments (t_i) [0];
				}

				var mi = Delegate.GetInvokeMethod (t_i);
				TypeReference rtype = mi.ReturnType;

				if (tic.IsReturnTypeNonDependent (mi, rtype)) {
					// It can be null for default arguments
					if (arguments[i] == null)
						continue;

					score -= tic.OutputTypeInference (ec, arguments[i].Expr, t_i);
				}
			}


			return DoSecondPhase (ec, tic, methodParameters, true);
		}
	}

}
